Khám phá thuật toán đồng thuận phân tán Raft: nguyên lý, pha hoạt động, cân nhắc triển khai thực tế và ứng dụng để xây dựng hệ thống toàn cầu, khả năng phục hồi cao.
Làm chủ đồng thuận phân tán: Cái nhìn chuyên sâu về triển khai thuật toán Raft cho các hệ thống toàn cầu
Trong thế giới ngày càng kết nối của chúng ta, các hệ thống phân tán là xương sống của hầu hết mọi dịch vụ kỹ thuật số, từ các nền tảng thương mại điện tử và tổ chức tài chính đến cơ sở hạ tầng điện toán đám mây và công cụ giao tiếp thời gian thực. Các hệ thống này mang lại khả năng mở rộng, tính sẵn sàng và khả năng phục hồi vô song bằng cách phân phối khối lượng công việc và dữ liệu trên nhiều máy. Tuy nhiên, sức mạnh này đi kèm với một thách thức đáng kể: đảm bảo rằng tất cả các thành phần đồng ý về trạng thái của hệ thống, ngay cả khi gặp phải độ trễ mạng, lỗi nút và các hoạt động đồng thời. Vấn đề cơ bản này được gọi là đồng thuận phân tán.
Đạt được đồng thuận trong một môi trường phân tán không đồng bộ, dễ xảy ra lỗi là một thách thức phức tạp. Trong nhiều thập kỷ, Paxos là thuật toán chiếm ưu thế để giải quyết thách thức này, được tôn sùng vì tính đúng đắn về mặt lý thuyết nhưng thường bị chỉ trích vì sự phức tạp và khó khăn trong việc triển khai. Sau đó, Raft xuất hiện, một thuật toán được thiết kế với mục tiêu chính: dễ hiểu. Raft hướng tới việc tương đương với Paxos về khả năng chịu lỗi và hiệu suất nhưng được cấu trúc theo cách dễ dàng hơn nhiều để các nhà phát triển nắm bắt và xây dựng.
Hướng dẫn toàn diện này đi sâu vào thuật toán Raft, khám phá các nguyên tắc cơ bản, cơ chế hoạt động, các cân nhắc triển khai thực tế và vai trò quan trọng của nó trong việc xây dựng các ứng dụng phân tán toàn cầu, mạnh mẽ. Cho dù bạn là một kiến trúc sư giàu kinh nghiệm, một kỹ sư hệ thống phân tán hay một nhà phát triển mong muốn xây dựng các dịch vụ có tính sẵn sàng cao, việc hiểu Raft là một bước thiết yếu để làm chủ sự phức tạp của điện toán hiện đại.
Sự cần thiết không thể thiếu của đồng thuận phân tán trong kiến trúc hiện đại
Hãy tưởng tượng một nền tảng thương mại điện tử toàn cầu xử lý hàng triệu giao dịch mỗi giây. Dữ liệu khách hàng, mức tồn kho, trạng thái đơn hàng—tất cả phải duy trì nhất quán trên nhiều trung tâm dữ liệu trải rộng khắp các châu lục. Sổ cái của một hệ thống ngân hàng, phân tán trên nhiều máy chủ, không thể chấp nhận ngay cả một sự bất đồng nhỏ về số dư tài khoản. Những kịch bản này làm nổi bật tầm quan trọng sống còn của đồng thuận phân tán.
Những thách thức cố hữu của hệ thống phân tán
Các hệ thống phân tán, theo bản chất của chúng, đưa ra vô số thách thức không có trong các ứng dụng nguyên khối. Hiểu rõ những thách thức này là rất quan trọng để đánh giá cao sự tinh tế và cần thiết của các thuật toán như Raft:
- Lỗi cục bộ: Không giống như một máy chủ duy nhất hoạt động hoặc lỗi hoàn toàn, một hệ thống phân tán có thể có một số nút bị lỗi trong khi các nút khác vẫn tiếp tục hoạt động. Một máy chủ có thể bị treo, kết nối mạng của nó có thể bị mất hoặc đĩa của nó có thể bị hỏng, trong khi phần còn lại của cụm vẫn hoạt động. Hệ thống phải tiếp tục hoạt động chính xác bất chấp những lỗi cục bộ này.
- Phân vùng mạng: Mạng kết nối các nút không phải lúc nào cũng đáng tin cậy. Phân vùng mạng xảy ra khi giao tiếp giữa các tập hợp con của các nút bị cắt đứt, khiến có vẻ như một số nút đã bị lỗi, ngay cả khi chúng vẫn đang chạy. Giải quyết các kịch bản "split-brain" này, nơi các phần khác nhau của hệ thống hoạt động độc lập dựa trên thông tin lỗi thời hoặc không nhất quán, là một vấn đề đồng thuận cốt lõi.
- Giao tiếp không đồng bộ: Các thông điệp giữa các nút có thể bị trễ, sắp xếp lại hoặc mất hoàn toàn. Không có đồng hồ toàn cầu hoặc đảm bảo về thời gian gửi thông điệp, gây khó khăn cho việc thiết lập một thứ tự sự kiện nhất quán hoặc trạng thái hệ thống xác định.
- Đồng thời: Nhiều nút có thể cố gắng cập nhật cùng một dữ liệu hoặc bắt đầu các hành động đồng thời. Nếu không có cơ chế phối hợp các hoạt động này, xung đột và không nhất quán là điều không thể tránh khỏi.
- Độ trễ không thể đoán trước: Đặc biệt trong các triển khai phân tán toàn cầu, độ trễ mạng có thể thay đổi đáng kể. Các hoạt động nhanh ở một khu vực có thể chậm ở một khu vực khác, ảnh hưởng đến các quy trình ra quyết định và phối hợp.
Tại sao đồng thuận là nền tảng của độ tin cậy
Các thuật toán đồng thuận cung cấp một khối xây dựng cơ bản để giải quyết những thách thức này. Chúng cho phép một tập hợp các thành phần không đáng tin cậy cùng nhau hoạt động như một đơn vị duy nhất, có độ tin cậy cao và mạch lạc. Cụ thể, đồng thuận giúp đạt được:
- Sao chép máy trạng thái (SMR): Ý tưởng cốt lõi đằng sau nhiều hệ thống phân tán chịu lỗi. Nếu tất cả các nút đồng ý về thứ tự hoạt động, và nếu mỗi nút bắt đầu ở cùng một trạng thái ban đầu và thực hiện các hoạt động đó theo cùng một thứ tự, thì tất cả các nút sẽ đạt được cùng một trạng thái cuối cùng. Đồng thuận là cơ chế để đồng ý về thứ tự hoạt động toàn cầu này.
- Tính sẵn sàng cao: Bằng cách cho phép một hệ thống tiếp tục hoạt động ngay cả khi một số ít nút bị lỗi, đồng thuận đảm bảo rằng các dịch vụ vẫn có thể truy cập và hoạt động, giảm thiểu thời gian ngừng hoạt động.
- Tính nhất quán dữ liệu: Nó đảm bảo rằng tất cả các bản sao dữ liệu vẫn được đồng bộ hóa, ngăn chặn các cập nhật xung đột và đảm bảo rằng khách hàng luôn đọc thông tin mới nhất và chính xác nhất.
- Khả năng chịu lỗi: Hệ thống có thể chịu được một số lỗi nút tùy ý (thường là lỗi treo) và tiếp tục hoạt động mà không cần sự can thiệp của con người.
Giới thiệu Raft: Một cách tiếp cận đồng thuận dễ hiểu
Raft ra đời từ thế giới học thuật với một mục tiêu rõ ràng: làm cho đồng thuận phân tán trở nên dễ tiếp cận. Các tác giả của nó, Diego Ongaro và John Ousterhout, đã thiết kế Raft một cách rõ ràng để dễ hiểu, nhằm mục đích cho phép triển khai rộng rãi hơn và chính xác các thuật toán đồng thuận.
Triết lý thiết kế cốt lõi của Raft: Dễ hiểu là ưu tiên hàng đầu
Raft chia nhỏ vấn đề đồng thuận phức tạp thành một số vấn đề con tương đối độc lập, mỗi vấn đề có tập hợp các quy tắc và hành vi riêng biệt. Tính mô-đun này hỗ trợ đáng kể cho việc hiểu. Các nguyên tắc thiết kế chính bao gồm:
- Tiếp cận lấy Lãnh đạo làm trung tâm: Không giống như một số thuật toán đồng thuận khác, nơi tất cả các nút tham gia bình đẳng vào việc ra quyết định, Raft chỉ định một lãnh đạo duy nhất. Lãnh đạo chịu trách nhiệm quản lý nhật ký được sao chép và phối hợp tất cả các yêu cầu của khách hàng. Điều này đơn giản hóa việc quản lý nhật ký và giảm sự phức tạp của các tương tác giữa các nút.
- Lãnh đạo mạnh: Lãnh đạo là cơ quan có thẩm quyền cao nhất để đề xuất các mục nhật ký mới và xác định thời điểm chúng được cam kết. Các nút theo dõi thụ động sao chép nhật ký của lãnh đạo và phản hồi các yêu cầu của lãnh đạo.
- Bầu cử có tính xác định: Raft sử dụng thời gian chờ bầu cử ngẫu nhiên để đảm bảo rằng thông thường chỉ có một ứng cử viên xuất hiện làm lãnh đạo trong một kỳ bầu cử nhất định.
- Tính nhất quán của nhật ký: Raft thực thi các thuộc tính nhất quán mạnh mẽ trên nhật ký được sao chép của nó, đảm bảo rằng các mục đã cam kết không bao giờ bị hoàn tác và tất cả các mục đã cam kết cuối cùng sẽ xuất hiện trên tất cả các nút có sẵn.
So sánh ngắn gọn với Paxos
Trước Raft, Paxos là tiêu chuẩn thực tế cho đồng thuận phân tán. Mặc dù mạnh mẽ, Paxos nổi tiếng là khó hiểu và triển khai chính xác. Thiết kế của nó, phân tách các vai trò (người đề xuất, người chấp nhận, người học) và cho phép nhiều lãnh đạo cùng tồn tại đồng thời (mặc dù chỉ một người có thể cam kết một giá trị), có thể dẫn đến các tương tác phức tạp và các trường hợp biên.
Raft, ngược lại, đơn giản hóa không gian trạng thái. Nó thực thi một mô hình lãnh đạo mạnh mẽ, nơi lãnh đạo chịu trách nhiệm cho tất cả các thay đổi nhật ký. Nó định nghĩa rõ ràng các vai trò (Lãnh đạo, Người theo dõi, Ứng cử viên) và các chuyển đổi giữa chúng. Cấu trúc này làm cho hành vi của Raft trực quan hơn và dễ lý giải hơn, dẫn đến ít lỗi triển khai hơn và chu kỳ phát triển nhanh hơn. Nhiều hệ thống trong thế giới thực ban đầu gặp khó khăn với Paxos đã thành công khi áp dụng Raft.
Ba vai trò cơ bản trong Raft
Tại bất kỳ thời điểm nào, mỗi máy chủ trong một cụm Raft đều ở một trong ba trạng thái: Lãnh đạo, Người theo dõi hoặc Ứng cử viên. Các vai trò này là độc quyền và động, với các máy chủ chuyển đổi giữa chúng dựa trên các quy tắc và sự kiện cụ thể.
1. Người theo dõi
- Vai trò thụ động: Người theo dõi là trạng thái thụ động nhất trong Raft. Họ chỉ đơn giản phản hồi các yêu cầu từ các lãnh đạo và ứng cử viên.
-
Nhận tin hiệu giữ kết nối (Heartbeats): Một người theo dõi mong đợi nhận được tin hiệu giữ kết nối (RPC AppendEntries trống) từ lãnh đạo theo định kỳ. Nếu một người theo dõi không nhận được tin hiệu giữ kết nối hoặc RPC AppendEntries trong một khoảng thời gian
election timeoutcụ thể, nó sẽ cho rằng lãnh đạo đã bị lỗi và chuyển sang trạng thái ứng cử viên. - Bỏ phiếu: Trong một cuộc bầu cử, một người theo dõi sẽ bỏ phiếu cho nhiều nhất một ứng cử viên mỗi kỳ.
- Sao chép nhật ký: Người theo dõi thêm các mục nhật ký vào nhật ký cục bộ của họ theo hướng dẫn của lãnh đạo.
2. Ứng cử viên
- Khởi xướng bầu cử: Khi một người theo dõi hết thời gian chờ (không nhận được thông tin từ lãnh đạo), nó chuyển sang trạng thái ứng cử viên để khởi xướng một cuộc bầu cử mới.
-
Tự bỏ phiếu: Một ứng cử viên tăng
current termcủa mình, bỏ phiếu cho chính mình và gửi RPCRequestVoteđến tất cả các máy chủ khác trong cụm. - Thắng cử: Nếu một ứng cử viên nhận được phiếu bầu từ đa số các máy chủ trong cụm cho cùng một kỳ, nó sẽ chuyển sang trạng thái lãnh đạo.
- Từ chức: Nếu một ứng cử viên phát hiện một máy chủ khác có kỳ cao hơn, hoặc nếu nó nhận được RPC AppendEntries từ một lãnh đạo hợp lệ, nó sẽ trở lại trạng thái người theo dõi.
3. Lãnh đạo
- Cơ quan duy nhất: Chỉ có một lãnh đạo trong một cụm Raft tại bất kỳ thời điểm nào (trong một kỳ nhất định). Lãnh đạo chịu trách nhiệm cho tất cả các tương tác của khách hàng, sao chép nhật ký và đảm bảo tính nhất quán.
-
Gửi tin hiệu giữ kết nối (Heartbeats): Lãnh đạo định kỳ gửi RPC
AppendEntries(heartbeats) đến tất cả những người theo dõi để duy trì quyền hạn của mình và ngăn chặn các cuộc bầu cử mới. - Quản lý nhật ký: Lãnh đạo chấp nhận các yêu cầu của khách hàng, thêm các mục nhật ký mới vào nhật ký cục bộ của mình, sau đó sao chép các mục này đến tất cả những người theo dõi.
- Cam kết: Lãnh đạo quyết định khi nào một mục được sao chép an toàn đến đa số máy chủ và có thể được cam kết với máy trạng thái.
-
Từ chức: Nếu lãnh đạo phát hiện một máy chủ có
termcao hơn, nó ngay lập tức từ chức và trở lại trạng thái người theo dõi. Điều này đảm bảo rằng hệ thống luôn tiến bộ với kỳ cao nhất được biết đến.
Các pha hoạt động của Raft: Hướng dẫn chi tiết
Raft hoạt động thông qua một chu trình liên tục của bầu cử lãnh đạo và sao chép nhật ký. Hai cơ chế chính này, cùng với các thuộc tính an toàn quan trọng, đảm bảo cụm duy trì tính nhất quán và khả năng chịu lỗi.
1. Bầu cử lãnh đạo
Quá trình bầu cử lãnh đạo là nền tảng cho hoạt động của Raft, đảm bảo rằng cụm luôn có một nút duy nhất, có thẩm quyền để phối hợp các hành động.
-
Thời gian chờ bầu cử: Mỗi người theo dõi duy trì một
election timeoutngẫu nhiên (thường là 150-300ms). Nếu một người theo dõi không nhận được bất kỳ thông tin liên lạc nào (heartbeat hoặc AppendEntries RPC) từ lãnh đạo hiện tại trong khoảng thời gian chờ này, nó sẽ cho rằng lãnh đạo đã bị lỗi hoặc một phân vùng mạng đã xảy ra. -
Chuyển sang Ứng cử viên: Khi hết thời gian chờ, người theo dõi chuyển sang trạng thái
Candidate. Nó tăngcurrent termcủa mình, bỏ phiếu cho chính mình và đặt lại bộ đếm thời gian bầu cử của mình. -
RPC RequestVote: Ứng cử viên sau đó gửi các RPC
RequestVoteđến tất cả các máy chủ khác trong cụm. RPC này bao gồmcurrent termcủa ứng cử viên,candidateIdcủa nó, và thông tin vềlast log indexvàlast log termcủa nó (sẽ nói rõ hơn về lý do tại sao điều này lại quan trọng đối với sự an toàn sau này). -
Quy tắc bỏ phiếu: Một máy chủ sẽ bỏ phiếu cho một ứng cử viên nếu:
-
current termcủa nó nhỏ hơn hoặc bằngtermcủa ứng cử viên. -
Nó chưa bỏ phiếu cho ứng cử viên nào khác trong
termhiện tại. -
Nhật ký của ứng cử viên ít nhất phải được cập nhật như nhật ký của chính nó. Điều này được xác định bằng cách so sánh
last log termtrước, sau đó làlast log indexnếu cáctermgiống nhau. Một ứng cử viên được coi là "up-to-date" nếu nhật ký của nó chứa tất cả các mục đã cam kết mà nhật ký của người bỏ phiếu chứa. Đây được gọi là hạn chế bầu cử và rất quan trọng đối với sự an toàn.
-
-
Thắng cử: Một ứng cử viên trở thành lãnh đạo mới nếu nó nhận được phiếu bầu từ đa số các máy chủ trong cụm cho cùng một kỳ. Sau khi được bầu, lãnh đạo mới ngay lập tức gửi các RPC
AppendEntries(heartbeats) đến tất cả các máy chủ khác để thiết lập quyền hạn của mình và ngăn chặn các cuộc bầu cử mới. -
Chia phiếu và thử lại: Có thể có nhiều ứng cử viên xuất hiện đồng thời, dẫn đến tình trạng chia phiếu nơi không ứng cử viên nào đạt được đa số. Để giải quyết vấn đề này, mỗi ứng cử viên có một thời gian chờ bầu cử ngẫu nhiên. Nếu thời gian chờ của một ứng cử viên hết hạn mà không thắng cử hoặc không nhận được thông tin từ một lãnh đạo mới, nó sẽ tăng
termcủa mình và bắt đầu một cuộc bầu cử mới. Sự ngẫu nhiên hóa giúp đảm bảo rằng các trường hợp chia phiếu hiếm khi xảy ra và được giải quyết nhanh chóng. -
Phát hiện các kỳ cao hơn: Nếu một ứng cử viên (hoặc bất kỳ máy chủ nào) nhận được một RPC với
termcao hơncurrent termcủa chính nó, nó ngay lập tức cập nhậtcurrent termcủa mình lên giá trị cao hơn và trở lại trạng tháifollower. Điều này đảm bảo rằng một máy chủ có thông tin lỗi thời không bao giờ cố gắng trở thành lãnh đạo hoặc làm gián đoạn một lãnh đạo hợp lệ.
2. Sao chép nhật ký
Khi một lãnh đạo được bầu, trách nhiệm chính của nó là quản lý nhật ký được sao chép và đảm bảo tính nhất quán trên toàn cụm. Điều này bao gồm việc chấp nhận các lệnh từ khách hàng, thêm chúng vào nhật ký của mình và sao chép chúng đến những người theo dõi.
- Yêu cầu từ khách hàng: Tất cả các yêu cầu từ khách hàng (các lệnh sẽ được thực thi bởi máy trạng thái) đều được gửi đến lãnh đạo. Nếu một khách hàng liên hệ với một người theo dõi, người theo dõi sẽ chuyển hướng yêu cầu đến lãnh đạo hiện tại.
-
Thêm vào nhật ký của lãnh đạo: Khi lãnh đạo nhận được một lệnh từ khách hàng, nó sẽ thêm lệnh đó như một
log entrymới vào nhật ký cục bộ của mình. Mỗi mục nhật ký chứa chính lệnh đó,termmà nó được nhận vàlog indexcủa nó. -
RPC AppendEntries: Lãnh đạo sau đó gửi các RPC
AppendEntriesđến tất cả những người theo dõi, yêu cầu họ thêm mục nhật ký mới (hoặc một loạt các mục) vào nhật ký của họ. Các RPC này bao gồm:-
term: Kỳ hiện tại của lãnh đạo. -
leaderId: ID của lãnh đạo (để người theo dõi chuyển hướng khách hàng). -
prevLogIndex: Chỉ số của mục nhật ký ngay trước các mục mới. -
prevLogTerm: Kỳ của mụcprevLogIndex. Hai tham số này (prevLogIndex,prevLogTerm) rất quan trọng đối với thuộc tính khớp nhật ký. -
entries[]: Các mục nhật ký để lưu trữ (trống đối với heartbeats). -
leaderCommit:commitIndexcủa lãnh đạo (chỉ số của mục nhật ký cao nhất được biết là đã cam kết).
-
-
Kiểm tra tính nhất quán (Thuộc tính khớp nhật ký): Khi một người theo dõi nhận được một RPC
AppendEntries, nó thực hiện một kiểm tra tính nhất quán. Nó xác minh xem nhật ký của mình có chứa một mục tạiprevLogIndexvới một kỳ khớp vớiprevLogTermhay không. Nếu kiểm tra này thất bại, người theo dõi sẽ từ chối RPCAppendEntries, thông báo cho lãnh đạo rằng nhật ký của nó không nhất quán. -
Giải quyết các sự không nhất quán: Nếu một người theo dõi từ chối RPC
AppendEntries, lãnh đạo sẽ giảmnextIndexcho người theo dõi đó và thử lại RPCAppendEntries.nextIndexlà chỉ số của mục nhật ký tiếp theo mà lãnh đạo sẽ gửi cho một người theo dõi cụ thể. Quá trình này tiếp tục cho đến khinextIndexđạt đến một điểm mà nhật ký của lãnh đạo và người theo dõi khớp nhau. Khi tìm thấy sự khớp, người theo dõi có thể chấp nhận các mục nhật ký tiếp theo, cuối cùng đưa nhật ký của nó nhất quán với nhật ký của lãnh đạo. -
Cam kết các mục: Một mục được coi là đã cam kết khi lãnh đạo đã sao chép thành công nó đến đa số các máy chủ (bao gồm cả chính nó). Sau khi được cam kết, mục có thể được áp dụng cho máy trạng thái cục bộ. Lãnh đạo cập nhật
commitIndexcủa mình và bao gồm nó trong các RPCAppendEntriestiếp theo để thông báo cho những người theo dõi về các mục đã cam kết. Những người theo dõi cập nhậtcommitIndexcủa họ dựa trênleaderCommitcủa lãnh đạo và áp dụng các mục lên đến chỉ số đó cho máy trạng thái của họ. - Thuộc tính hoàn chỉnh của lãnh đạo: Raft đảm bảo rằng nếu một mục nhật ký được cam kết trong một kỳ nhất định, thì tất cả các lãnh đạo tiếp theo cũng phải có mục nhật ký đó. Thuộc tính này được thực thi bởi hạn chế bầu cử: một ứng cử viên chỉ có thể thắng cử nếu nhật ký của nó ít nhất được cập nhật như đa số các máy chủ khác. Điều này ngăn chặn việc một lãnh đạo có thể bị bầu mà có thể ghi đè hoặc bỏ lỡ các mục đã cam kết.
3. Thuộc tính và đảm bảo an toàn
Sự mạnh mẽ của Raft bắt nguồn từ một số thuộc tính an toàn được thiết kế cẩn thận nhằm ngăn chặn sự không nhất quán và đảm bảo tính toàn vẹn dữ liệu:
- An toàn bầu cử: Tối đa chỉ có một lãnh đạo có thể được bầu trong một kỳ nhất định. Điều này được thực thi bởi cơ chế bỏ phiếu, nơi một người theo dõi chỉ cấp nhiều nhất một phiếu bầu mỗi kỳ và một ứng cử viên cần đa số phiếu bầu.
- Tính hoàn chỉnh của lãnh đạo: Nếu một mục nhật ký đã được cam kết trong một kỳ nhất định, thì mục đó sẽ có mặt trong nhật ký của tất cả các lãnh đạo tiếp theo. Điều này rất quan trọng để ngăn chặn mất dữ liệu đã cam kết và chủ yếu được đảm bảo bởi hạn chế bầu cử.
- Thuộc tính khớp nhật ký: Nếu hai nhật ký chứa một mục có cùng chỉ số và kỳ, thì các nhật ký đó giống hệt nhau ở tất cả các mục trước đó. Điều này đơn giản hóa việc kiểm tra tính nhất quán của nhật ký và cho phép lãnh đạo cập nhật nhật ký của người theo dõi một cách hiệu quả.
- An toàn cam kết: Sau khi một mục được cam kết, nó sẽ không bao giờ bị hoàn tác hoặc ghi đè. Đây là một hệ quả trực tiếp của các thuộc tính Hoàn chỉnh của Lãnh đạo và Khớp nhật ký. Khi một mục được cam kết, nó được coi là được lưu trữ vĩnh viễn.
Các khái niệm và cơ chế chính trong Raft
Ngoài các vai trò và giai đoạn hoạt động, Raft dựa vào một số khái niệm cốt lõi để quản lý trạng thái và đảm bảo tính đúng đắn.
1. Các kỳ (Terms)
Một term trong Raft là một số nguyên tăng liên tục. Nó hoạt động như một đồng hồ logic cho cụm. Mỗi term bắt đầu bằng một cuộc bầu cử, và nếu một cuộc bầu cử thành công, một lãnh đạo duy nhất sẽ được bầu cho term đó. Các term rất quan trọng để xác định thông tin cũ và đảm bảo rằng các máy chủ luôn ưu tiên thông tin mới nhất:
-
Các máy chủ trao đổi
current termcủa họ trong tất cả các RPC. -
Nếu một máy chủ phát hiện một máy chủ khác có
termcao hơn, nó sẽ cập nhậtcurrent termcủa chính nó và trở lại trạng tháifollower. -
Nếu một ứng cử viên hoặc lãnh đạo phát hiện
termcủa mình đã lỗi thời (thấp hơntermcủa máy chủ khác), nó sẽ ngay lập tức từ chức.
2. Các mục nhật ký (Log Entries)
log là thành phần trung tâm của Raft. Đó là một chuỗi các mục được sắp xếp, trong đó mỗi log entry đại diện cho một lệnh sẽ được thực thi bởi máy trạng thái. Mỗi mục chứa:
- Lệnh: Hoạt động thực tế cần thực hiện (ví dụ: "set x=5", "create user").
- Term: Kỳ mà mục được tạo trên lãnh đạo.
- Index: Vị trí của mục trong nhật ký. Các mục nhật ký được sắp xếp nghiêm ngặt theo chỉ số.
Nhật ký là bền vững, có nghĩa là các mục được ghi vào bộ nhớ ổn định trước khi phản hồi cho khách hàng, bảo vệ chống mất dữ liệu trong quá trình treo máy.
3. Máy trạng thái (State Machine)
Mỗi máy chủ trong một cụm Raft duy trì một state machine. Đây là một thành phần cụ thể của ứng dụng xử lý các mục nhật ký đã cam kết. Để đảm bảo tính nhất quán, máy trạng thái phải là có tính xác định (được cung cấp cùng một trạng thái ban đầu và chuỗi lệnh, nó luôn tạo ra cùng một đầu ra và trạng thái cuối cùng) và lũy đẳng (áp dụng cùng một lệnh nhiều lần có cùng tác dụng như áp dụng một lần, điều này giúp xử lý lại một cách duyên dáng, mặc dù cam kết nhật ký của Raft phần lớn đảm bảo một lần áp dụng).
4. Chỉ số cam kết (Commit Index)
commitIndex là chỉ số mục nhật ký cao nhất được biết là đã cam kết. Điều này có nghĩa là nó đã được sao chép an toàn đến đa số máy chủ và có thể được áp dụng cho máy trạng thái. Các lãnh đạo xác định commitIndex, và những người theo dõi cập nhật commitIndex của họ dựa trên các RPC AppendEntries của lãnh đạo. Tất cả các mục cho đến commitIndex được coi là vĩnh viễn và không thể hoàn tác.
5. Ảnh chụp nhanh (Snapshots)
Theo thời gian, nhật ký được sao chép có thể trở nên rất lớn, tiêu tốn đáng kể dung lượng đĩa và khiến việc sao chép và phục hồi nhật ký trở nên chậm chạp. Raft giải quyết vấn đề này bằng snapshots. Một ảnh chụp nhanh là một biểu diễn nhỏ gọn trạng thái của máy trạng thái tại một thời điểm cụ thể. Thay vì giữ toàn bộ nhật ký, các máy chủ có thể định kỳ "chụp nhanh" trạng thái của chúng, loại bỏ tất cả các mục nhật ký cho đến điểm ảnh chụp nhanh, sau đó sao chép ảnh chụp nhanh đó cho các người theo dõi mới hoặc bị tụt hậu. Quá trình này cải thiện đáng kể hiệu quả:
- Nhật ký nhỏ gọn: Giảm lượng dữ liệu nhật ký bền vững.
- Phục hồi nhanh hơn: Các máy chủ mới hoặc bị treo có thể nhận một ảnh chụp nhanh thay vì phát lại toàn bộ nhật ký từ đầu.
-
RPC InstallSnapshot: Raft định nghĩa một RPC
InstallSnapshotđể truyền các ảnh chụp nhanh từ lãnh đạo đến những người theo dõi.
Mặc dù hiệu quả, việc chụp ảnh nhanh làm tăng độ phức tạp cho việc triển khai, đặc biệt trong việc quản lý việc tạo ảnh chụp nhanh đồng thời, cắt bớt nhật ký và truyền tải.
Triển khai Raft: Những cân nhắc thực tế cho triển khai toàn cầu
Việc chuyển đổi thiết kế tinh tế của Raft thành một hệ thống mạnh mẽ, sẵn sàng sản xuất, đặc biệt cho đối tượng toàn cầu và cơ sở hạ tầng đa dạng, liên quan đến việc giải quyết một số thách thức kỹ thuật thực tế.
1. Độ trễ và phân vùng mạng trong bối cảnh toàn cầu
Đối với các hệ thống phân tán toàn cầu, độ trễ mạng là một yếu tố đáng kể. Một cụm Raft thường yêu cầu đa số các nút phải đồng ý về một mục nhật ký trước khi nó có thể được cam kết. Trong một cụm trải rộng khắp các châu lục, độ trễ giữa các nút có thể lên đến hàng trăm mili giây. Điều này ảnh hưởng trực tiếp đến:
- Độ trễ cam kết: Thời gian để một yêu cầu của khách hàng được cam kết có thể bị thắt cổ chai bởi liên kết mạng chậm nhất đến đa số bản sao. Các chiến lược như các người theo dõi chỉ đọc (không yêu cầu tương tác với lãnh đạo cho các lần đọc cũ) hoặc cấu hình quorum nhận biết địa lý (ví dụ: 3 nút ở một khu vực, 2 nút ở khu vực khác cho một cụm 5 nút, nơi đa số có thể nằm trong một khu vực nhanh duy nhất) có thể giảm thiểu điều này.
-
Tốc độ bầu cử lãnh đạo: Độ trễ cao có thể làm chậm các RPC
RequestVote, có khả năng dẫn đến việc chia phiếu thường xuyên hơn hoặc thời gian bầu cử lâu hơn. Điều chỉnh thời gian chờ bầu cử lớn hơn đáng kể so với độ trễ liên nút điển hình là rất quan trọng. - Xử lý phân vùng mạng: Mạng thực tế dễ bị phân vùng. Raft xử lý các phân vùng một cách chính xác bằng cách đảm bảo rằng chỉ phân vùng chứa đa số máy chủ mới có thể bầu một lãnh đạo và tiến hành. Phân vùng thiểu số sẽ không thể cam kết các mục mới, do đó ngăn chặn các kịch bản split-brain. Tuy nhiên, các phân vùng kéo dài trong thiết lập phân tán toàn cầu có thể dẫn đến không khả dụng ở một số khu vực, đòi hỏi các quyết định kiến trúc cẩn thận về vị trí quorum.
2. Lưu trữ bền vững và độ bền
Tính đúng đắn của Raft phụ thuộc rất nhiều vào tính bền vững của nhật ký và trạng thái của nó. Trước khi một máy chủ phản hồi RPC hoặc áp dụng một mục vào máy trạng thái của nó, nó phải đảm bảo rằng dữ liệu liên quan (các mục nhật ký, current term, votedFor) được ghi vào bộ nhớ ổn định và fsync'd (ghi vào đĩa). Điều này ngăn ngừa mất dữ liệu trong trường hợp treo máy. Các cân nhắc bao gồm:
- Hiệu suất: Ghi đĩa thường xuyên có thể là một nút cổ chai về hiệu suất. Ghi hàng loạt và sử dụng SSD hiệu suất cao là các tối ưu hóa phổ biến.
- Độ tin cậy: Lựa chọn một giải pháp lưu trữ mạnh mẽ và bền vững (đĩa cục bộ, lưu trữ gắn mạng, lưu trữ khối đám mây) là rất quan trọng.
- WAL (Write-Ahead Log): Thông thường, các triển khai Raft sử dụng nhật ký ghi trước (write-ahead log) để đảm bảo độ bền, tương tự như cơ sở dữ liệu, để đảm bảo rằng các thay đổi được ghi vào đĩa trước khi được áp dụng vào bộ nhớ.
3. Tương tác của khách hàng và các mô hình nhất quán
Khách hàng tương tác với cụm Raft bằng cách gửi yêu cầu đến lãnh đạo. Xử lý các yêu cầu của khách hàng bao gồm:
- Phát hiện lãnh đạo: Khách hàng cần một cơ chế để tìm ra lãnh đạo hiện tại. Điều này có thể thông qua một cơ chế phát hiện dịch vụ, một điểm cuối cố định chuyển hướng, hoặc bằng cách thử các máy chủ cho đến khi một máy chủ phản hồi với vai trò lãnh đạo.
- Thử lại yêu cầu: Khách hàng phải sẵn sàng thử lại yêu cầu nếu lãnh đạo thay đổi hoặc nếu lỗi mạng xảy ra.
-
Tính nhất quán đọc: Raft chủ yếu đảm bảo tính nhất quán mạnh mẽ cho các ghi. Đối với các đọc, có một số mô hình khả thi:
- Đọc nhất quán mạnh: Khách hàng có thể yêu cầu lãnh đạo đảm bảo trạng thái của nó được cập nhật bằng cách gửi một heartbeat đến đa số các người theo dõi của nó trước khi phục vụ một đọc. Điều này đảm bảo tính tươi mới nhưng tăng độ trễ.
- Đọc theo Leader-Lease: Lãnh đạo có thể lấy một 'lease' từ đa số các nút trong một khoảng thời gian ngắn, trong thời gian đó nó biết rằng nó vẫn là lãnh đạo và có thể phục vụ các đọc mà không cần thêm đồng thuận. Điều này nhanh hơn nhưng có giới hạn thời gian.
- Đọc dữ liệu cũ (từ người theo dõi): Đọc trực tiếp từ người theo dõi có thể cung cấp độ trễ thấp hơn nhưng có nguy cơ đọc dữ liệu cũ nếu nhật ký của người theo dõi bị chậm hơn lãnh đạo. Điều này có thể chấp nhận được đối với các ứng dụng mà tính nhất quán cuối cùng là đủ cho các đọc.
4. Thay đổi cấu hình (Thành viên cụm)
Việc thay đổi thành viên của một cụm Raft (thêm hoặc xóa máy chủ) là một hoạt động phức tạp cũng phải được thực hiện thông qua đồng thuận để tránh sự không nhất quán hoặc các kịch bản split-brain. Raft đề xuất một kỹ thuật gọi là Đồng thuận chung (Joint Consensus):
- Hai cấu hình: Trong quá trình thay đổi cấu hình, hệ thống tạm thời hoạt động với hai cấu hình chồng chéo: cấu hình cũ (C_old) và cấu hình mới (C_new).
- Trạng thái đồng thuận chung (C_old, C_new): Lãnh đạo đề xuất một mục nhật ký đặc biệt đại diện cho cấu hình chung. Khi mục này được cam kết (yêu cầu sự đồng thuận từ đa số trong cả C_old và C_new), hệ thống ở trạng thái chuyển tiếp. Bây giờ, các quyết định yêu cầu đa số từ cả hai cấu hình. Điều này đảm bảo rằng trong quá trình chuyển tiếp, cả cấu hình cũ và mới đều không thể đưa ra quyết định đơn phương, ngăn chặn sự phân kỳ.
- Chuyển sang C_new: Khi mục nhật ký cấu hình chung được cam kết, lãnh đạo đề xuất một mục nhật ký khác chỉ đại diện cho cấu hình mới (C_new). Khi mục thứ hai này được cam kết, cấu hình cũ bị loại bỏ và hệ thống hoạt động hoàn toàn dưới C_new.
- An toàn: Quá trình cam kết hai pha này đảm bảo rằng không lúc nào có thể có hai lãnh đạo xung đột được bầu (một theo C_old, một theo C_new) và rằng hệ thống vẫn hoạt động trong suốt quá trình thay đổi.
Việc triển khai thay đổi cấu hình một cách chính xác là một trong những phần thử thách nhất của việc triển khai Raft do vô số trường hợp biên và kịch bản lỗi trong trạng thái chuyển tiếp.
5. Kiểm thử hệ thống phân tán: Một cách tiếp cận nghiêm ngặt
Kiểm thử một thuật toán đồng thuận phân tán như Raft là vô cùng thách thức do tính chất không xác định của nó và vô số chế độ lỗi. Các kiểm thử đơn vị đơn giản là không đủ. Kiểm thử nghiêm ngặt bao gồm:
- Chèn lỗi: Hệ thống đưa vào các lỗi như treo nút, phân vùng mạng, độ trễ thông điệp và sắp xếp lại thông điệp. Các công cụ như Jepsen được thiết kế đặc biệt cho mục đích này.
- Kiểm thử dựa trên thuộc tính: Định nghĩa các bất biến và thuộc tính an toàn (ví dụ: tối đa một lãnh đạo mỗi kỳ, các mục đã cam kết không bao giờ bị mất) và kiểm thử rằng việc triển khai tuân thủ các điều này trong các điều kiện khác nhau.
- Kiểm tra mô hình: Đối với các phần quan trọng của thuật toán, các kỹ thuật kiểm chứng hình thức có thể được sử dụng để chứng minh tính đúng đắn, mặc dù điều này rất chuyên biệt.
- Môi trường mô phỏng: Chạy thử nghiệm trong các môi trường mô phỏng các điều kiện mạng (độ trễ, mất gói) điển hình của các triển khai toàn cầu.
Các trường hợp sử dụng và ứng dụng thực tế
Tính thực tiễn và dễ hiểu của Raft đã dẫn đến việc nó được áp dụng rộng rãi trên nhiều thành phần cơ sở hạ tầng quan trọng khác nhau:
1. Kho lưu trữ khóa-giá trị phân tán và sao chép cơ sở dữ liệu
- etcd: Một thành phần nền tảng của Kubernetes, etcd sử dụng Raft để lưu trữ và sao chép dữ liệu cấu hình, thông tin phát hiện dịch vụ và quản lý trạng thái của cụm. Độ tin cậy của nó là tối quan trọng để Kubernetes hoạt động chính xác.
- Consul: Được phát triển bởi HashiCorp, Consul sử dụng Raft cho phần backend lưu trữ phân tán của nó, cho phép phát hiện dịch vụ, kiểm tra tình trạng và quản lý cấu hình trong các môi trường cơ sở hạ tầng động.
- TiKV: Kho lưu trữ khóa-giá trị giao dịch phân tán được sử dụng bởi TiDB (một cơ sở dữ liệu SQL phân tán) triển khai Raft để đảm bảo sao chép dữ liệu và tính nhất quán.
- CockroachDB: Cơ sở dữ liệu SQL phân tán toàn cầu này sử dụng Raft rộng rãi để sao chép dữ liệu trên nhiều nút và khu vực địa lý, đảm bảo tính sẵn sàng cao và tính nhất quán mạnh mẽ ngay cả khi đối mặt với các lỗi trên toàn khu vực.
2. Phát hiện dịch vụ và quản lý cấu hình
Raft cung cấp một nền tảng lý tưởng cho các hệ thống cần lưu trữ và phân phối siêu dữ liệu quan trọng về các dịch vụ và cấu hình trên một cụm. Khi một dịch vụ đăng ký hoặc cấu hình của nó thay đổi, Raft đảm bảo rằng tất cả các nút cuối cùng đồng ý về trạng thái mới, cho phép cập nhật động mà không cần can thiệp thủ công.
3. Bộ điều phối giao dịch phân tán
Đối với các hệ thống yêu cầu tính nguyên tử trên nhiều hoạt động hoặc dịch vụ, Raft có thể là nền tảng cho các bộ điều phối giao dịch phân tán, đảm bảo rằng các nhật ký giao dịch được sao chép nhất quán trước khi cam kết các thay đổi trên các bên tham gia.
4. Phối hợp cụm và bầu cử lãnh đạo trong các hệ thống khác
Ngoài việc sử dụng cơ sở dữ liệu hoặc kho lưu trữ khóa-giá trị rõ ràng, Raft thường được nhúng dưới dạng thư viện hoặc thành phần cốt lõi để quản lý các tác vụ phối hợp, bầu chọn lãnh đạo cho các quy trình phân tán khác, hoặc cung cấp một mặt phẳng điều khiển đáng tin cậy trong các hệ thống lớn hơn. Chẳng hạn, nhiều giải pháp điện toán đám mây bản địa tận dụng Raft để quản lý trạng thái của các thành phần mặt phẳng điều khiển của chúng.
Ưu điểm và nhược điểm của Raft
Mặc dù Raft mang lại những lợi ích đáng kể, điều cần thiết là phải hiểu các đánh đổi của nó.
Ưu điểm:
- Dễ hiểu: Mục tiêu thiết kế chính của nó, giúp dễ dàng triển khai, gỡ lỗi và lý giải hơn so với các thuật toán đồng thuận cũ hơn như Paxos.
- Tính nhất quán mạnh: Cung cấp các đảm bảo nhất quán mạnh mẽ cho các mục nhật ký đã cam kết, đảm bảo tính toàn vẹn và độ tin cậy của dữ liệu.
-
Khả năng chịu lỗi: Có thể chịu đựng lỗi của một số ít nút (tối đa
(N-1)/2lỗi trong một cụmNnút) mà không làm mất tính khả dụng hoặc nhất quán. - Hiệu suất: Trong điều kiện ổn định (không thay đổi lãnh đạo), Raft có thể đạt được thông lượng cao vì lãnh đạo xử lý tất cả các yêu cầu tuần tự và sao chép song song, tận dụng băng thông mạng một cách hiệu quả.
- Vai trò được xác định rõ ràng: Các vai trò rõ ràng (Lãnh đạo, Người theo dõi, Ứng cử viên) và các chuyển đổi trạng thái đơn giản hóa mô hình tư duy và triển khai.
- Thay đổi cấu hình: Cung cấp một cơ chế mạnh mẽ (Đồng thuận chung) để thêm hoặc xóa các nút khỏi cụm một cách an toàn mà không ảnh hưởng đến tính nhất quán.
Nhược điểm:
- Nút cổ chai lãnh đạo: Tất cả các yêu cầu ghi của khách hàng phải thông qua lãnh đạo. Trong các kịch bản với thông lượng ghi cực cao hoặc khi các lãnh đạo ở xa khách hàng về mặt địa lý, điều này có thể trở thành nút cổ chai về hiệu suất.
- Độ trễ đọc: Đạt được các đọc nhất quán mạnh mẽ thường yêu cầu giao tiếp với lãnh đạo, có khả năng tăng độ trễ. Đọc từ các người theo dõi có nguy cơ đọc dữ liệu cũ.
- Yêu cầu quorum: Yêu cầu đa số các nút phải khả dụng để cam kết các mục mới. Trong một cụm 5 nút, 2 lỗi có thể chấp nhận được. Nếu 3 nút bị lỗi, cụm sẽ không khả dụng cho các ghi. Điều này có thể thách thức trong các môi trường bị phân vùng cao hoặc phân tán địa lý rộng, nơi việc duy trì đa số trên các khu vực là khó khăn.
- Độ nhạy mạng: Rất nhạy cảm với độ trễ và phân vùng mạng, điều này có thể ảnh hưởng đến thời gian bầu cử và thông lượng hệ thống tổng thể, đặc biệt trong các triển khai phân tán rộng rãi.
- Sự phức tạp của thay đổi cấu hình: Mặc dù mạnh mẽ, cơ chế Đồng thuận chung là một trong những phần phức tạp hơn của thuật toán Raft để triển khai chính xác và kiểm thử kỹ lưỡng.
- Điểm lỗi duy nhất (cho ghi): Mặc dù chịu lỗi đối với lỗi của lãnh đạo, nếu lãnh đạo bị ngừng hoạt động vĩnh viễn và không thể bầu một lãnh đạo mới (ví dụ: do phân vùng mạng hoặc quá nhiều lỗi), hệ thống không thể tiến hành các ghi.
Kết luận: Làm chủ đồng thuận phân tán cho các hệ thống toàn cầu bền vững
Thuật toán Raft là một minh chứng cho sức mạnh của thiết kế chu đáo trong việc đơn giản hóa các vấn đề phức tạp. Sự nhấn mạnh vào tính dễ hiểu của nó đã dân chủ hóa đồng thuận phân tán, cho phép nhiều nhà phát triển và tổ chức hơn xây dựng các hệ thống có tính sẵn sàng cao và chịu lỗi mà không phải chịu đựng sự phức tạp khó hiểu của các phương pháp tiếp cận trước đây.
Từ việc điều phối các cụm container với Kubernetes (thông qua etcd) đến việc cung cấp lưu trữ dữ liệu bền vững cho các cơ sở dữ liệu toàn cầu như CockroachDB, Raft là một "ngựa thồ" thầm lặng, đảm bảo rằng thế giới kỹ thuật số của chúng ta luôn nhất quán và hoạt động. Việc triển khai Raft không phải là một công việc nhỏ, nhưng sự rõ ràng trong đặc tả của nó và sự phong phú của hệ sinh thái xung quanh khiến nó trở thành một nỗ lực đáng giá cho những ai cam kết xây dựng thế hệ cơ sở hạ tầng mạnh mẽ, có khả năng mở rộng tiếp theo.
Những hiểu biết có thể hành động cho các nhà phát triển và kiến trúc sư:
- Ưu tiên hiểu biết: Trước khi cố gắng triển khai, hãy dành thời gian để hiểu kỹ lưỡng từng quy tắc và chuyển đổi trạng thái của Raft. Bài báo gốc và các giải thích trực quan là những tài nguyên vô giá.
- Tận dụng các thư viện hiện có: Đối với hầu hết các ứng dụng, hãy cân nhắc sử dụng các triển khai Raft hiện có đã được kiểm duyệt kỹ lưỡng (ví dụ: từ etcd, thư viện Raft của HashiCorp) thay vì tự xây dựng từ đầu, trừ khi yêu cầu của bạn rất chuyên biệt hoặc bạn đang tiến hành nghiên cứu học thuật.
- Kiểm thử nghiêm ngặt là không thể thương lượng: Chèn lỗi, kiểm thử dựa trên thuộc tính và mô phỏng rộng rãi các kịch bản lỗi là tối quan trọng đối với bất kỳ hệ thống đồng thuận phân tán nào. Đừng bao giờ cho rằng "nó hoạt động" mà không phá vỡ nó một cách kỹ lưỡng.
- Thiết kế cho độ trễ toàn cầu: Khi triển khai toàn cầu, hãy cân nhắc kỹ lưỡng việc đặt quorum, cấu trúc liên kết mạng và các chiến lược đọc của khách hàng để tối ưu hóa cả tính nhất quán và hiệu suất trên các khu vực địa lý khác nhau.
-
Tính bền vững và độ bền: Đảm bảo lớp lưu trữ cơ bản của bạn mạnh mẽ và các thao tác
fsynchoặc tương đương được sử dụng đúng cách để ngăn ngừa mất dữ liệu trong các kịch bản treo máy.
Khi các hệ thống phân tán tiếp tục phát triển, các nguyên tắc được thể hiện bởi Raft—sự rõ ràng, mạnh mẽ và khả năng chịu lỗi—sẽ vẫn là nền tảng của kỹ thuật phần mềm đáng tin cậy. Bằng cách làm chủ Raft, bạn trang bị cho mình một công cụ mạnh mẽ để xây dựng các ứng dụng có khả năng phục hồi, có thể mở rộng toàn cầu, có thể chịu được sự hỗn loạn không thể tránh khỏi của điện toán phân tán.